package com.daoliuhe.scaffold.realms;

import com.daoliuhe.scaffold.model.Role;
import com.daoliuhe.scaffold.service.UserService;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.ldap.JndiLdapRealm;
import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.realm.ldap.LdapUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
import java.util.HashSet;
import java.util.Set;

@Component
public class ShiroLDAPRealm extends JndiLdapRealm {

    private static final Logger logger = LoggerFactory.getLogger(ShiroLDAPRealm.class);

    @Autowired
    UserService userService;

    @Override
    protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token,
                                                            LdapContextFactory ldapContextFactory) throws NamingException {
        logger.info("queryForAuthenticationInfo");
        Object principal = token.getPrincipal();
        Object credentials = token.getCredentials();
        String rootDN = "dc=daoliuhe,dc=com";

        LdapContext systemCtx = null;
        LdapContext ctx = null;
        try {
            systemCtx = ldapContextFactory.getSystemLdapContext();

            SearchControls constraints = new SearchControls();
            constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
            NamingEnumeration<?> results = systemCtx.search("", "(sAMAccountName=" + principal + ")", constraints);
            if (results == null || !results.hasMore()) {
                logger.error("throw new UnknownAccountException()");
                throw new UnknownAccountException();
            } else {
                while (null != results && results.hasMoreElements()) {
                    Object obj = results.nextElement();
                    if (obj instanceof SearchResult) {
                        SearchResult si = (SearchResult) obj;
                        principal = si.getName() + "," + rootDN;
                    }
                }
                logger.info("sAMAccountName, principal:{}", principal);
                try {
                    ctx = ldapContextFactory.getLdapContext(principal, credentials);
                } catch (NamingException e) {
                    e.printStackTrace();
                    logger.error("NamingException, e getStackTrace:{}", e.getStackTrace().toString());
                    throw new IncorrectCredentialsException();
                }
                AuthenticationInfo info = createAuthenticationInfo(token, principal, credentials, ctx);
                return info;
            }
        } finally {
            LdapUtils.closeContext(systemCtx);
            LdapUtils.closeContext(ctx);
        }
    }

    @Override
    protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals,
                                                          LdapContextFactory ldapContextFactory) throws NamingException {
        String loginId = (String) principals.getPrimaryPrincipal();
        logger.info("queryForAuthorizationInfo, loginId:{}", loginId);
        //角色集合
        Set<String> roles = new HashSet<String>();
        //权限集合
        Set<String> permissions = new HashSet<String>();
        //授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //角色集合
        Set<Role> userRoles = userService.getRolesByLoginId(loginId);
        for (Role role : userRoles) {
            roles.add(role.getName());
        }
        //roles.addAll(userService.getRolesByUserLoginId(loginId));
        logger.debug("queryForAuthorizationInfo, roles:{}", roles.toString());
        //权限集合
        permissions.addAll(userService.getPermissionsByRoles(userRoles));
        logger.debug("queryForAuthorizationInfo, permissions:{}", permissions.toString());

        info.setStringPermissions(permissions);
        info.setRoles(roles);
        return info;
    }

}
